﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WarehouseManagement.BusinessLogic.DTO;
using WarehouseManagement.BusinessLogic.DTOConverter;
using WarehouseManagement.Shared;

namespace WarehouseManagement.BusinessLogic.BC
{
    public class ExportBC : BaseBC
    {
        private UserBC _userBC;
        private MerchandiseBC _merchandiseBC;
        private CustomerBC _custBC;
        private RevenueBC _revenueBC;
        private DataChangedLogBC _dataLogBC;
        private StockBC _stockBC;

        public ExportBC()
        {
            _userBC = new UserBC();
            _merchandiseBC = new MerchandiseBC();
            _custBC = new CustomerBC();
            _revenueBC = new RevenueBC();
            _dataLogBC = new DataChangedLogBC();
            _stockBC = new StockBC();
        }

        public string GenerateNewExportNumber()
        {
            decimal no = 1;
            var today = DateTime.Now;
            var prefix = "PX";

            var list = Context.Exports.OrderByDescending(e => e.CreatedDate).ToList();
            if (list.Count() > 0)
            {
                var first = list.FirstOrDefault();
                if (first != null)
                {
                    var lastNo = decimal.Parse(first.ExportNumber.Substring(10, 6));
                    if (first.CreatedDate.Month == today.Month && first.CreatedDate.Year == today.Year)
                        no = lastNo + 1;

                }
            }

            var newNumber = NumberGenerator.GenerateNumber(prefix, today.Month, today.Year, no);
            return newNumber;

        }

        public void UpdateExport(ExportDTO export)
        {
            var exportEntity = Context.Exports.SingleOrDefault(e => e.Id == export.Id);
            if (export.CustomerId != null)
                exportEntity.Customer = Context.Customers.SingleOrDefault(e => e.Id == export.CustomerId.Value);
            else
                exportEntity.Customer = null;
            exportEntity.ExportReason = export.ExportReason;
            exportEntity.Notes = export.Notes;

            SubmitChanges();

        }

        public void AddExport(ExportDTO export, IEnumerable<ExportDetailDTO> details)
        {

            if (!Context.Exports.Any(e => e.ExportNumber == export.ExportNumber))
            {
                var importEntity = ExportConverter.Convert(export);
                Context.Exports.InsertOnSubmit(importEntity);
                this.SubmitChanges();
            }
            else
            {
                UpdateExport(export);
            }

            foreach (var item in details)
            {
                item.ExportId = export.Id;
                item.CreatedDate = DateTime.Now;
                item.LastModifiedDate = item.CreatedDate;
                item.CreatedBy = GlobalMemory.CurrentLoginUser;
                item.LastModifiedUser = item.CreatedBy;

                var d = ExportConverter.Convert(item);

                if (!Context.ExportDetails.Any(e => e.Id == item.Id))
                {
                    Context.ExportDetails.InsertOnSubmit(d);
                    this.SubmitChanges();
                }

                _dataLogBC.SetExportDataChanged(item.MerchandiseId, export.ExportDate.Month, export.ExportDate.Year, true);

                // Tính tồn cuối kỳ tháng hiện tại (Đầu kỳ tháng sau)
                var today = export.ExportDate;
                var stockInfo = _stockBC.GetViewStockInfo(item.MerchandiseId,
                    new DateTime(today.Year, today.Month, 1),
                    new DateTime(today.Year, today.Month, DateTime.DaysInMonth(today.Year, today.Month)));

                var nextMonth = new DateTime(today.Year, today.Month, 1).AddMonths(1);
                _stockBC.AppendMerchandiseInStock(
                    item.MerchandiseId,
                    nextMonth.Month,
                    nextMonth.Year,
                  stockInfo.EndPeriodQuantity,
                  stockInfo.EndPeriodValue);
                _stockBC.UpdateAllMerchandisesInStockAfterDate(
                    item.MerchandiseId,
                    export.ExportDate.Month,
                    export.ExportDate.Year);


                item.IsSaved = true;
            }

        }

        public void UpdateExportDetail(ExportDetailDTO item)
        {
            var exportDetail = Context.ExportDetails.SingleOrDefault(e => e.Id == item.Id);


            //if (item.MerchandiseId == exportDetail.MerchandiseId)
            //{
            //    var quantity = (item.Quantity - exportDetail.Quantity) * -1;

            //    var oldValue = exportDetail.OriginalPrice * (decimal)exportDetail.Quantity;
            //    var newValue = item.OriginalPrice * (decimal)item.Quantity;
            //    var value = (newValue - oldValue) * -1;

            //    _stockBC.AppendMerchandiseInStock(
            //        item.MerchandiseId,
            //        exportDetail.Export.ExportDate.Month,
            //        exportDetail.Export.ExportDate.Year,
            //                   quantity, value);
            //    _stockBC.UpdateAllMerchandisesInStockAfterDate(
            //        item.MerchandiseId,
            //         exportDetail.Export.ExportDate.Month,
            //        exportDetail.Export.ExportDate.Year,
            //          quantity, value);


            //}
            //else
            //{
            //    var quantityToUpdate = exportDetail.Quantity;
            //    var valueToUpdate = (decimal)exportDetail.Quantity * exportDetail.OriginalPrice;
            //    _stockBC.AppendMerchandiseInStock(
            //        exportDetail.MerchandiseId,
            //         exportDetail.Export.ExportDate.Month,
            //        exportDetail.Export.ExportDate.Year,
            //            quantityToUpdate,
            //            valueToUpdate);
            //    _stockBC.UpdateAllMerchandisesInStockAfterDate(
            //        exportDetail.MerchandiseId,
            //         exportDetail.Export.ExportDate.Month,
            //        exportDetail.Export.ExportDate.Year,
            //            quantityToUpdate, valueToUpdate);

            //    quantityToUpdate = item.Quantity * -1;
            //    valueToUpdate = (decimal)item.Quantity * item.OriginalPrice * -1;
            //    _stockBC.AppendMerchandiseInStock(
            //        item.MerchandiseId,
            //         exportDetail.Export.ExportDate.Month,
            //        exportDetail.Export.ExportDate.Year,
            //         quantityToUpdate, valueToUpdate);
            //    _stockBC.UpdateAllMerchandisesInStockAfterDate(
            //        item.MerchandiseId,
            //         exportDetail.Export.ExportDate.Month,
            //        exportDetail.Export.ExportDate.Year,
            //            quantityToUpdate, valueToUpdate);


            //    //// Update revenue - cũ + mới
            //    //// - cũ
            //    //var qty2 = -exportDetail.Quantity;
            //    //var oldValue2 = exportDetail.ExportPrice * (decimal)exportDetail.Quantity * -1;
            //    //var oldProfit2 = (exportDetail.ExportPrice - exportDetail.OriginalPrice) * (decimal)exportDetail.Quantity * -1;
            //    //_revenueBC.UpdateMonthlyRevenue(exportDetail.MerchandiseId,
            //    //    exportDetail.Export.ExportDate.Month,
            //    //    exportDetail.Export.ExportDate.Year,
            //    //    qty2,
            //    //    oldValue2,
            //    //   oldProfit2);

            //    //// + mới
            //    //qty2 = item.Quantity;
            //    //oldValue2 = item.ExportPrice * (decimal)item.Quantity;
            //    //oldProfit2 = (item.ExportPrice - item.OriginalPrice) * (decimal)item.Quantity;
            //    //_revenueBC.UpdateMonthlyRevenue(item.MerchandiseId,
            //    //    exportDetail.Export.ExportDate.Month,
            //    //    exportDetail.Export.ExportDate.Year,
            //    //    qty2,
            //    //    oldValue2,
            //    //   oldProfit2);

            //}


            exportDetail.ExportPrice = item.ExportPrice;
            exportDetail.Quantity = item.Quantity;
            exportDetail.LastModifiedDate = DateTime.Now;
            exportDetail.OriginalPrice = item.OriginalPrice;
            exportDetail.LastModifiedUser = GlobalMemory.CurrentLoginUser;
            exportDetail.Merchandise = Context.Merchandises.SingleOrDefault(e => e.Id == item.MerchandiseId);
            this.SubmitChanges();

            //Mark export data changed
            _dataLogBC.SetExportDataChanged(item.MerchandiseId, exportDetail.Export.ExportDate.Month,
                exportDetail.Export.ExportDate.Year, true);



            //================================================================
            // Tính tồn cuối kỳ tháng hiện tại (Đầu kỳ tháng sau)
            var today = exportDetail.Export.ExportDate;
            var stockInfo = _stockBC.GetViewStockInfo(item.MerchandiseId,
                new DateTime(today.Year, today.Month, 1),
                new DateTime(today.Year, today.Month, DateTime.DaysInMonth(today.Year, today.Month)));

            var nextMonth = new DateTime(today.Year, today.Month, 1).AddMonths(1);
            _stockBC.AppendMerchandiseInStock(
                item.MerchandiseId,
                nextMonth.Month,
                nextMonth.Year,
              stockInfo.EndPeriodQuantity,
              stockInfo.EndPeriodValue);
            _stockBC.UpdateAllMerchandisesInStockAfterDate(
                item.MerchandiseId,
                exportDetail.Export.ExportDate.Month,
                exportDetail.Export.ExportDate.Year);

            item.IsSaved = true;
        }

        public void UpdateExportDetails(IEnumerable<ExportDetailDTO> items, ExportDTO master)
        {
            var import = Context.Exports.SingleOrDefault(e => e.ExportNumber == master.ExportNumber);
            if (import != null)
            {
                import.ExportReason = master.ExportReason;
                import.Notes = master.Notes;
            }
            SubmitChanges();

            foreach (var item in items)
            {
                UpdateExportDetail(item);
            }

            UpdateExport(master);
        }

        public void DeleteExportDetail(Guid detailId)
        {
            var item = Context.ExportDetails.SingleOrDefault(e => e.Id == detailId);
            if (item != null)
            {
                //var quantityToUpdate = item.Quantity;
                //var valueToUpdate = item.OriginalPrice * (decimal)item.Quantity;
                //_stockBC.AppendMerchandiseInStock(
                //    item.MerchandiseId,
                //    item.Export.ExportDate.Month,
                //    item.Export.ExportDate.Year,
                //   quantityToUpdate, valueToUpdate);
                //_stockBC.UpdateAllMerchandisesInStockAfterDate(
                //    item.MerchandiseId,
                //    item.Export.ExportDate.Month,
                //    item.Export.ExportDate.Year,
                //   quantityToUpdate, valueToUpdate);

                var today = item.Export.ExportDate;
                var merchandiseId = item.MerchandiseId;

                Context.ExportDetails.DeleteOnSubmit(item);
                this.SubmitChanges();

                //Mark export data changed
                _dataLogBC.SetExportDataChanged(merchandiseId, today.Month,
                    today.Year, true);

                //================================================================
                // Tính tồn cuối kỳ tháng hiện tại (Đầu kỳ tháng sau)

                var stockInfo = _stockBC.GetViewStockInfo(merchandiseId,
                    new DateTime(today.Year, today.Month, 1),
                    new DateTime(today.Year, today.Month, DateTime.DaysInMonth(today.Year, today.Month)));

                var nextMonth = new DateTime(today.Year, today.Month, 1).AddMonths(1);
                _stockBC.AppendMerchandiseInStock(
                    merchandiseId,
                    nextMonth.Month,
                    nextMonth.Year,
                  stockInfo.EndPeriodQuantity,
                  stockInfo.EndPeriodValue);
                _stockBC.UpdateAllMerchandisesInStockAfterDate(
                    merchandiseId,
                    today.Month,
                    today.Year);

            }
        }

        public void DeleteExportDetails(IEnumerable<Guid> detailIds)
        {
            foreach (var id in detailIds)
            {
                DeleteExportDetail(id);
            }

        }

        private void DeleteExport(Guid importId)
        {
            var Export = Context.Exports.SingleOrDefault(e => e.Id == importId);
            if (Export != null)
            {
                Context.Exports.DeleteOnSubmit(Export);
                SubmitChanges();
            }
        }

        public void SaveExport(ExportDTO masterObj, IEnumerable<ExportDetailDTO> submittedRecords, bool editMode = false)
        {
            try
            {
                this.BeginTransaction();

                var allItems = Context.ExportDetails.Where(e => e.Export.ExportNumber == masterObj.ExportNumber)
                    .Select(e => ExportConverter.Convert(e))
                    .ToList();

                var addedItems = GetAddedItems(allItems, submittedRecords).ToList();
                var deletedItems = GetDeletedItems(allItems, submittedRecords).ToList();
                var needUpdatedItems = GetNeedUpdatedItems(addedItems, deletedItems, submittedRecords).ToList();

                if (addedItems.Count > 0)
                {
                    this.AddExport(masterObj, addedItems);
                }
                if (editMode)
                {
                    if (needUpdatedItems.Count > 0)
                    {
                        this.UpdateExportDetails(needUpdatedItems, masterObj);
                    }
                    if (deletedItems.Count > 0)
                    {
                        this.DeleteExportDetails(deletedItems.Select(e => e.Id));
                        var ExportInDb = Context.Exports.SingleOrDefault(e => e.Id == masterObj.Id);
                        if (ExportInDb.ExportDetails.Count == 0)
                            DeleteExport(ExportInDb.Id);
                    }
                }


                this.CommitTransaction();
            }
            catch (Exception ex)
            {
                this.RollbackTransaction();
                throw ex;
            }
        }

        private dynamic GetExportInformation(int merchandiseId, DateTime from, DateTime to)
        {
            var list = Context.ExportDetails.Where(e => e.MerchandiseId == merchandiseId
                && !e.IsCancelled
                && (e.Export.ExportDate.Date >= from.Date && e.Export.ExportDate.Date <= to.Date));
            dynamic result = new { TotalExportQuantity = 0, TotalExportOriginValue = 0, TotalExportValue = 0 };
            if (list.Count() > 0)
            {
                var qty = list.Sum(e => e.Quantity);
                var value = list.Sum(e => (decimal)e.Quantity * e.ExportPrice);
                var originValue = list.Sum(e => (decimal)e.Quantity * e.OriginalPrice);
                result = new { TotalExportQuantity = qty, TotalExportOriginValue = originValue, TotalExportValue = value };
            }
            return result;
        }

        private void EnsureMonthlyExportData(int merchandiseId, int month, int year)
        {
            ImportBC importBC = new ImportBC();

            if (_dataLogBC.IsExportDataChanged(merchandiseId, month, year))
            {
                var updatedExportInfo = GetExportInformation(merchandiseId, new DateTime(year, month, 1),
                     new DateTime(year, month, DateTime.DaysInMonth(year, month)));

                importBC.UpdateMonthlyImportExport(merchandiseId, month, year,
                    exportQty: updatedExportInfo.TotalExportQuantity,
                    exportValue: updatedExportInfo.TotalExportValue,
                    profit: updatedExportInfo.TotalExportValue - updatedExportInfo.TotalExportOriginValue);

                _dataLogBC.SetImportDataChanged(merchandiseId, month, year, false);

            }

        }

        public dynamic GetExportInformationOptimized(int merchandiseId, DateTime from, DateTime to)
        {
            double qty = 0;
            decimal value = 0;
            decimal originValue = 0;

            ImportBC importBC = new ImportBC();

            DateTime t = from.Date;
            while (t <= to.Date)
            {
                if (t == new DateTime(t.Year, t.Month, 1) && t.AddDays(DateTime.DaysInMonth(t.Year, t.Month) - 1) <= to.Date)
                {
                    EnsureMonthlyExportData(merchandiseId, t.Month, t.Year);

                    var obj = importBC.GetImportExportInformationByMonth(merchandiseId, t.Month, t.Year);
                    if (obj != null)
                    {
                        qty += obj.ExportQuantity;
                        value += obj.ExportValue;
                        originValue += (obj.ExportValue - obj.Profit);
                    }

                    t = t.AddMonths(1);
                }
                else
                {
                    var toDate = new DateTime(t.Year, t.Month, DateTime.DaysInMonth(t.Year, t.Month));
                    if (toDate > to.Date)
                        toDate = to.Date;

                    var obj = GetExportInformation(merchandiseId, t.Date, toDate);
                    qty += obj.TotalExportQuantity;
                    value += obj.TotalExportValue;
                    originValue += obj.TotalExportOriginValue;

                    t = toDate.AddDays(1);
                }


            }
            importBC = null;

            return new { TotalExportValue = value, TotalExportQuantity = qty, TotalExportOriginValue = originValue };
        }

        private IEnumerable<ExportDetailDTO> GetDeletedItems(IEnumerable<ExportDetailDTO> allItems, IEnumerable<ExportDetailDTO> submitted)
        {
            var idList = allItems.Select(e => e.Id)
                .Except(submitted.Select(e => e.Id));
            foreach (var id in idList)
            {
                yield return allItems.SingleOrDefault(e => e.Id == id);
            }
        }

        private IEnumerable<ExportDetailDTO> GetAddedItems(IEnumerable<ExportDetailDTO> allItems, IEnumerable<ExportDetailDTO> submitted)
        {
            var idList = submitted.Select(e => e.Id)
                .Except(allItems.Select(e => e.Id));
            foreach (var id in idList)
            {
                yield return submitted.SingleOrDefault(e => e.Id == id);
            }
        }

        private IEnumerable<ExportDetailDTO> GetNeedUpdatedItems(
            IEnumerable<ExportDetailDTO> added,
            IEnumerable<ExportDetailDTO> deleted,
            IEnumerable<ExportDetailDTO> submitted)
        {
            var addedAndDeleted = added.Select(e => e.Id).Union(deleted.Select(e => e.Id));
            var needUpdatedIds = submitted.Select(e => e.Id).Except(addedAndDeleted);
            foreach (var id in needUpdatedIds)
            {
                yield return submitted.SingleOrDefault(e => e.Id == id);
            }
        }

        public ExportDTO GetExportByNumber(string number)
        {
            var obj = (Context.Exports.SingleOrDefault(e => e.ExportNumber == number));
            if (obj != null) return ExportConverter.Convert(obj);
            return null;
        }


        public IEnumerable<ViewExportDetailDTO> GetExportList(DateTime from, DateTime to)
        {
            var list = Context.ExportDetails.Where(e => e.Export.ExportDate.Date >= from.Date && e.Export.ExportDate.Date <= to.Date)
                .AsEnumerable()
                .Select(e => new ViewExportDetailDTO()
                {
                    ExportDate = e.Export.ExportDate,
                    ExportId = e.Export.Id,
                    ExportNumber = e.Export.ExportNumber,
                    ExportUnitPrice = e.ExportPrice,
                    OriginUnitPrice = e.OriginalPrice,
                    MerchandiseId = e.Merchandise.Id,
                    MerchandiseCategory = e.Merchandise.MerchandiseCategory.MerchandiseCategoryName,
                    CustomerName = (e.Export.Customer != null ?
                       string.Format("{0} {1}", e.Export.Customer.LastName, e.Export.Customer.FirstName) : ""),
                    MerchandiseName = e.Merchandise.MerchandiseName,
                    Quantity = e.Quantity,
                    Unit = e.Merchandise.Unit,
                    TotalUnitPrice = (decimal)e.Quantity * e.ExportPrice,
                    Profit = (e.ExportPrice - e.OriginalPrice) * (decimal)e.Quantity
                });
            return list.ToList();
        }

        public IEnumerable<ExportDetailDTO> GetExportDetailList(string exportNumber)
        {
            var list = Context.ExportDetails.Where(e => e.Export.ExportNumber == exportNumber)
                .AsEnumerable()
                .Select(e => ExportConverter.Convert(e))
                .ToList();
            foreach (var item in list)
            {
                var obj = _stockBC.GetMerchandiseInStockCurrentTime(item.MerchandiseId, DateTime.Now);
                //var orderedQty = _merchandiseBC.GetOrderedQuantity(item.MerchandiseId);
                var orderedQty = 0;
                item.AvailableExportQuantity = obj.QuantityInStock - orderedQty;
            }

            return list;

        }

        public void GetTotalExportValueInDay(DateTime date, out decimal exportVal, out decimal profitVal, int? merchandiseId = null)
        {
            exportVal = profitVal = 0;
            var list = Context.ExportDetails.Where(e => e.Export.ExportDate.Date == date.Date);
            if (merchandiseId.HasValue)
            {
                list = list.Where(e => e.MerchandiseId == merchandiseId.Value);
            }

            if (list.Count() > 0)
            {
                exportVal = list.Sum(e => (decimal)e.ExportPrice * (decimal)e.Quantity);
                profitVal = list.Sum(e => (decimal)e.Profit * (decimal) e.Quantity);
            }
        }
    }
}
